package com.ng.common.aop;

import com.jfinal.plugin.activerecord.ActiveRecordException;
import com.jfinal.plugin.activerecord.Config;
import com.jfinal.plugin.activerecord.DbKit;
import com.jfinal.plugin.activerecord.NestedTransactionHelpException;
import com.jfinal.plugin.activerecord.tx.TxConfig;
import lombok.extern.slf4j.Slf4j;
import org.aspectj.lang.ProceedingJoinPoint;
import org.aspectj.lang.annotation.Around;
import org.aspectj.lang.annotation.Aspect;
import org.aspectj.lang.annotation.Pointcut;
import org.aspectj.lang.reflect.MethodSignature;
import org.springframework.stereotype.Component;
import java.lang.reflect.Method;
import java.sql.Connection;
import java.sql.SQLException;

@Slf4j
@Aspect
@Component
public class JFinalTxAop {

	/**
	 * 自定义JFinal 事物注解 value中的意思解释
	 * 
	 * @within 表示注解在类下面所有的方法 ， 暂时不使用这种方式
	 */
	@Pointcut("@annotation(com.ng.common.annotation.Tx)")
	private void method() {
	}

	@Around(value = "method()", argNames = "pjp")
	public Object doAround(ProceedingJoinPoint pjp) throws Throwable {
		Object retVal = null;
		Config config = getConfigWithTxConfig(pjp);
		if (config == null)
			config = DbKit.getConfig();

		Connection conn = config.getThreadLocalConnection();
		if (conn != null) {
			try {
				if (conn.getTransactionIsolation() < getTransactionLevel(config))
					conn.setTransactionIsolation(getTransactionLevel(config));
				retVal = pjp.proceed();
				return retVal;
			} catch (SQLException e) {
				throw new ActiveRecordException(e);
			}
		}

		Boolean autoCommit = null;
		try {
			conn = config.getConnection();
			autoCommit = conn.getAutoCommit();
			config.setThreadLocalConnection(conn);
			conn.setTransactionIsolation(getTransactionLevel(config));// conn.setTransactionIsolation(transactionLevel);

			conn.setAutoCommit(false);
			retVal = pjp.proceed();
			conn.commit();
		} catch (NestedTransactionHelpException e) {
			if (conn != null)
				try {
					conn.rollback();
				} catch (Exception e1) {
					log.error(e1.getMessage(), e1);
				}
			log.error(e.getMessage(), e);
		} catch (Throwable t) {
			if (conn != null)
				try {
					conn.rollback();
				} catch (Exception e1) {
					log.error(e1.getMessage(), e1);
				}
			throw t instanceof RuntimeException ? (RuntimeException) t : new ActiveRecordException(t);
		} finally {
			try {
				if (conn != null) {
					if (autoCommit != null)
						conn.setAutoCommit(autoCommit);
					conn.close();
				}
			} catch (Throwable t) {
				// can not throw exception here, otherwise the more important exception in
				// previous catch block can not be thrown
				log.error(t.getMessage(), t);
			} finally {
				// prevent memory leak
				config.removeThreadLocalConnection();
			}
		}
		return retVal;
	}

	/**
	 * 获取配置的事务级别
	 * 
	 * @param config
	 * @return
	 */
	protected int getTransactionLevel(Config config) {
		return config.getTransactionLevel();
	}

	/**
	 * @param pjp
	 * @return Config
	 */
	public static Config getConfigWithTxConfig(ProceedingJoinPoint pjp) {
		MethodSignature ms = (MethodSignature) pjp.getSignature();
		Method method = ms.getMethod();
		TxConfig txConfig = method.getAnnotation(TxConfig.class);
		if (txConfig == null)
			txConfig = pjp.getTarget().getClass().getAnnotation(TxConfig.class);

		if (txConfig != null) {
			Config config = DbKit.getConfig(txConfig.value());
			if (config == null)
				throw new RuntimeException("Config not found with TxConfig: " + txConfig.value());
			return config;
		}
		return null;
	}
}
